package arm.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
import javax.management.loading.MLet;

import mx4j.tools.adaptor.http.HttpAdaptor;
import mx4j.tools.adaptor.http.XSLTProcessor;

import org.apache.log4j.Logger;

import arm.AppResourceManager;
import arm.ArmMBeanRespository;
import arm.mbean.ArmConnectorServer;
import arm.mbean.ArmMBeanServer;
import arm.mbean.ArmRegistry;
import arm.util.PropertiesUtil;
import arm.ArmException;

import com.sun.jdmk.comm.HtmlAdaptorServer;

public class StdAppResourceManager implements AppResourceManager,StdAppResourceManagerMBean,MBeanRegistration{

	private static final Logger log = Logger.getLogger(StdAppResourceManager.class);
	private static final String PROP_ARM_INSTANCE_NAME="arm.instance.name";
	private static final String PROP_ARM_MBEANSERVER_OBJECTNAME="arm.mbeanserver.objectname";
	private static final String PROP_ARM_MBEANSERVER_LOCALSERVER="arm.mbeanserver.localserver";
	private static final String PROP_ARM_RMI_HOST="arm.rmi.host";
	private static final String PROP_ARM_RMI_PORT="arm.rmi.port";
	private static final String PROP_ARM_CONNECTOR_SERVICE_URL="arm.connector.service.url.name";
	private static final String PROP_ARM_CONNECTOR_OWN_THREAD="arm.connector.own.thread";
	private static final String PROP_ARM_CONNECTOR_THREAD_DEAMON="arm.connector.thread.deamon";
	private static final String PROP_ARM_CONNECTOR_MBEAN_OBJECTNAME="arm.connector.mbean.objectname";
	private static final String PROP_ARM_CONNECTOR_ENV_JMX_REMOTE_X_PASSWORD_FILE="jmx.remote.x.password.file";
	private static final String PROP_ARM_CONNECTOR_ENV_JMX_REMOTE_X_ACCESS_FILE="jmx.remote.x.access.file";
	private static final String PROP_ARM_ADAPTER_HTML="arm.adapter.html";
	private static final String PROP_ARM_ADAPTER_HTML_PROXY="arm.adapter.html.proxy";
	private static final String PROP_ARM_ADAPTER_HTML_PORT="arm.adapter.html.port";
	private static final String PROP_ARM_ADAPTER_HTML_OBJECTNAME="arm.adapter.html.objectname";
	private static final String PROP_ARM_ADAPTER_MLET="arm.adapter.mlet";
	private static final String PROP_ARM_ADAPTER_MLET_OBJECTNAME="arm.adapter.mlet.objectname";
	private MBeanServer mbeanServer;
	private String objectName;
	private ObjectName objName;
	private ArmMBeanServer armMBeanServer;
	private ArmMBeanRespository armMBeanRespository;
	private static final String OBJECT_NAME="ARM:name=arm.instance";
	private Properties prop;
	private PropertiesUtil pu;
	private boolean localExisteServer;
	private String mbeanServerDomain;
    private ArmRegistry armRegistry;
    private int rmiport;
    private String rmihost;
    private ArmConnectorServer armConnectorServer;
    private String connectorName;
    private Map environment = new HashMap();
    private String remotepassfile;
    private String remoteacessfile;
    private boolean ownthread;
    private boolean deamon;
    private String connectorObjectName;
    private HtmlAdaptorServer htmlAdaptorServer;
    private boolean usehtmladapter;
    private int htmlport;
    private String htmldomain;
    private boolean useMlet;
    private MLet mlet;
    private String mletdomain;
    private String instanceName;
    private String hostip;
    private Date upTime;
    private Date downTime;
    private String htmlAdaptorProxy;
    private HttpAdaptor httpAdaptor;
    private enum STATE{INIT,CONFIG,START,RUNNING,STOP};
    private STATE state=STATE.STOP;
    
	public StdAppResourceManager() {
	}

	public StdAppResourceManager(String objectName) {
		this.objectName = objectName;
	}


	private void init()
	{
		state = STATE.INIT;
		prop = new Properties();
		InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("arm.properties");
		if(is ==null)
		{
			log.info("arm.properties not found in classpath");
			ClassLoader cl = this.getClass().getClassLoader();
			is = cl.getResourceAsStream("arm/arm.properties");
			if(is==null)
			{
				new ArmException("arm.properties not found");
			}
			else
			{
				try {
					prop.load(is);
					log.info("Default load arm/arm.propreties file");
				} catch (IOException e) {
					new ArmException("arm/arm.propertis load faild");
				}
			}
		}
		else
		{
			try {
				prop.load(is);
				log.info("load classpath arm.properties file finished");
			} catch (IOException e) {
				new ArmException("load arm.properties exception "+e.getMessage());
			}
		}
		overSystemProperties();
		try {
			hostip = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
		}
		armMBeanRespository = ArmMBeanRespository.getInstance();
	}
	
	private void overSystemProperties() {
		Properties sysprop =null;
		try{
		sysprop = System.getProperties();
		}catch(Exception e)
		{
			log.info("system properties not Permissions access");
		}
		if(sysprop!=null)
		{
			prop.putAll(sysprop);
		}
		state = STATE.CONFIG;
	}

	public void start() throws Exception {
		if(state!=STATE.STOP)
		{
			throw new Exception(this.instanceName +" already "+state.name()+"  cann`t start");
		}
		init();
		state = STATE.START;
		pu = new PropertiesUtil(prop);
		armMBeanServer = new ArmMBeanServer();
		this.localExisteServer = pu.getBooleanProperty(this.PROP_ARM_MBEANSERVER_LOCALSERVER);
		armMBeanServer.setLocalExisteServer(localExisteServer);
		mbeanServerDomain =pu.getStringProperty(this.PROP_ARM_MBEANSERVER_OBJECTNAME);
		armMBeanServer.setDefaultdomain(mbeanServerDomain);
		mbeanServer = armMBeanServer.createMBeanServer();
		armRegistry = new ArmRegistry();
		rmiport = pu.getIntegerProperty(this.PROP_ARM_RMI_PORT);
		rmihost = pu.getStringProperty(this.PROP_ARM_RMI_HOST);
		armRegistry.setHost(rmihost);
		armRegistry.setRmiport(rmiport);
		armRegistry.createRegistry();
		
		armConnectorServer = new ArmConnectorServer();
		armConnectorServer.setMbeanServer(mbeanServer);
		connectorName = pu.getStringProperty(this.PROP_ARM_CONNECTOR_SERVICE_URL);
		if(connectorName!=null)
		{
			if(rmiport!=0)
			{
				connectorName = "service:jmx:rmi://"+hostip+"/jndi/rmi://"+hostip+":"+rmiport+"/"+connectorName;
			}
		}
		armConnectorServer.setServiceURL(connectorName);
		this.remotepassfile = pu.getStringProperty(this.PROP_ARM_CONNECTOR_ENV_JMX_REMOTE_X_PASSWORD_FILE);
		if(remotepassfile!=null)
		{
		    environment.put(this.PROP_ARM_CONNECTOR_ENV_JMX_REMOTE_X_PASSWORD_FILE, remotepassfile);
		}
		
		this.remoteacessfile = pu.getStringProperty(this.PROP_ARM_CONNECTOR_ENV_JMX_REMOTE_X_ACCESS_FILE);
		if(remoteacessfile!=null)
		{
			environment.put(this.PROP_ARM_CONNECTOR_ENV_JMX_REMOTE_X_ACCESS_FILE, remoteacessfile);
		}
		armConnectorServer.setEnvironment(environment);
		ownthread = pu.getBooleanProperty(this.PROP_ARM_CONNECTOR_OWN_THREAD);
		armConnectorServer.setThreaded(ownthread);
		deamon = pu.getBooleanProperty(this.PROP_ARM_CONNECTOR_THREAD_DEAMON);
		armConnectorServer.setDeamon(deamon);
		connectorObjectName = pu.getStringProperty(this.PROP_ARM_CONNECTOR_MBEAN_OBJECTNAME);
		if(connectorObjectName!=null)
		{
			ObjectName connectorName = ObjectName.getInstance("Arm:name="+connectorObjectName);
			armConnectorServer.setObjName(connectorName);
			armMBeanRespository.addMBeanToRespository(connectorName);
		}
		armConnectorServer.createConnectorServer();
		
		objectName = pu.getStringProperty(this.PROP_ARM_MBEANSERVER_OBJECTNAME);
		instanceName = pu.getStringProperty(this.PROP_ARM_INSTANCE_NAME);
		if (objectName != null) {
			objName = new ObjectName("Arm:name="+objectName);
		} else {
			if(instanceName!=null)
			{
				objName = new ObjectName("Arm:name="+instanceName);
			}
			else
			{
			    objName = new ObjectName(this.OBJECT_NAME);
			}
		}
		mbeanServer.registerMBean(this, objName);
		armMBeanRespository.addMBeanToRespository(objName);
		
		this.usehtmladapter = pu.getBooleanProperty(this.PROP_ARM_ADAPTER_HTML);
		htmlAdaptorProxy = pu.getStringProperty(this.PROP_ARM_ADAPTER_HTML_PROXY);
		if(usehtmladapter)
		{
			if(htmlAdaptorProxy!=null&&htmlAdaptorProxy.equalsIgnoreCase("xm4j"))
			{
				this.createMx4jHtmlAdapter();
			}
			else
			{
				htmlAdaptorServer = new HtmlAdaptorServer();
				this.htmlport = pu.getIntegerProperty(this.PROP_ARM_ADAPTER_HTML_PORT);
				if(htmlport!=0)
				{
					htmlAdaptorServer.setPort(htmlport);
				}
				this.htmldomain = pu.getStringProperty(this.PROP_ARM_ADAPTER_HTML_OBJECTNAME);
				if(htmldomain!=null)
				{
					ObjectName htmlObjectName = ObjectName.getInstance("Arm:name="+htmldomain);
					mbeanServer.registerMBean(htmlAdaptorServer, htmlObjectName);
					armMBeanRespository.addMBeanToRespository(htmlObjectName);
				}
				htmlAdaptorServer.start();
				log.info("HtmAdaptorServer is started http URL:http://"+hostip+":"+htmlAdaptorServer.getPort()+"//");
			}
		}
		this.useMlet = pu.getBooleanProperty(PROP_ARM_ADAPTER_MLET);
		if(useMlet)
		{
			mlet = new MLet();
			this.mletdomain = pu.getStringProperty(this.PROP_ARM_ADAPTER_MLET_OBJECTNAME);
			if(mletdomain!=null)
			{
				ObjectName mletObjName = ObjectName.getInstance("Arm:name="+mletdomain);
				mbeanServer.registerMBean(mlet, mletObjName);
				armMBeanRespository.addMBeanToRespository(mletObjName);
			}
			log.info("MLet registry MBeanServer finished");
		}
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		upTime = new Date();
		state = STATE.RUNNING;
		log.info("arm start finished at "+sdf.format(upTime));
	}

	public void stop() throws IOException, MBeanRegistrationException, InstanceNotFoundException {

		if(state == STATE.STOP)
		{
			throw new InstanceNotFoundException(this.instanceName+" already "+state.name() +" cann`t stop");
		}
		if(htmlAdaptorServer!=null&&htmlAdaptorServer.isActive())
		{
			htmlAdaptorServer.stop();
		}
		if(httpAdaptor!=null&&httpAdaptor.isActive())
		{
			httpAdaptor.stop();
		}
		if(armConnectorServer!=null&&armConnectorServer.isActive())
		{
			armConnectorServer.stop();
		}
		if(mbeanServer!=null)
		{
			Set<ObjectName> set = ArmMBeanRespository.respository;
			for(ObjectName objName :set)
			{
				mbeanServer.unregisterMBean(objName);
			}
		}
		state = STATE.STOP;
		downTime = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		log.info("arm shutdown is "+sdf.format(downTime));
	}


	public void setObjectName(String objectName) {
		this.objectName = objectName;
	}

	public void setMbeanServer(MBeanServer mbeanServer) {
		this.mbeanServer = mbeanServer;
	}




	public ObjectInstance createMBean(String className, ObjectName name)
			throws ReflectionException, InstanceAlreadyExistsException,
			MBeanRegistrationException, MBeanException,
			NotCompliantMBeanException {
		return mbeanServer.createMBean(className, name);
	}

	public ObjectInstance createMBean(String className, ObjectName name,
			ObjectName loaderName) throws ReflectionException,
			InstanceAlreadyExistsException, MBeanRegistrationException,
			MBeanException, NotCompliantMBeanException,
			InstanceNotFoundException {
		return mbeanServer.createMBean(className, name, loaderName);
	}

	public ObjectInstance createMBean(String className, ObjectName name,
			Object[] params, String[] signature) throws ReflectionException,
			InstanceAlreadyExistsException, MBeanRegistrationException,
			MBeanException, NotCompliantMBeanException {
		return mbeanServer.createMBean(className, name, params, signature);
	}

	public ObjectInstance createMBean(String className, ObjectName name,
			ObjectName loaderName, Object[] params, String[] signature)
			throws ReflectionException, InstanceAlreadyExistsException,
			MBeanRegistrationException, MBeanException,
			NotCompliantMBeanException, InstanceNotFoundException {
		return mbeanServer.createMBean(className, name, loaderName, params, signature);
	}

	public ObjectInstance registerMBean(Object object, ObjectName name)
			throws InstanceAlreadyExistsException, MBeanRegistrationException,
			NotCompliantMBeanException {
		return mbeanServer.registerMBean(object, name);
	}

	public void unregisterMBean(ObjectName name)
			throws InstanceNotFoundException, MBeanRegistrationException {

		mbeanServer.unregisterMBean(name);
	}

	public ObjectInstance getObjectInstance(ObjectName name)
			throws InstanceNotFoundException {
		return mbeanServer.getObjectInstance(name);
	}

	public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
		return mbeanServer.queryMBeans(name, query);
	}

	public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
		return mbeanServer.queryNames(name, query);
	}

	public boolean isRegistered(ObjectName name) {
		return mbeanServer.isRegistered(name);
	}

	public Integer getMBeanCount() {
		return mbeanServer.getMBeanCount();
	}

	public Object getAttribute(ObjectName name, String attribute)
			throws MBeanException, AttributeNotFoundException,
			InstanceNotFoundException, ReflectionException {
		return mbeanServer.getAttribute(name, attribute);
	}

	public AttributeList getAttributes(ObjectName name, String[] attributes)
			throws InstanceNotFoundException, ReflectionException {
		
		return mbeanServer.getAttributes(name, attributes);
	}

	public void setAttribute(ObjectName name, Attribute attribute)
			throws InstanceNotFoundException, AttributeNotFoundException,
			InvalidAttributeValueException, MBeanException, ReflectionException {
		mbeanServer.setAttribute(name, attribute);
		
	}

	public AttributeList setAttributes(ObjectName name, AttributeList attributes)
			throws InstanceNotFoundException, ReflectionException {
		return mbeanServer.setAttributes(name, attributes);
	}

	public Object invoke(ObjectName name, String operationName,
			Object[] params, String[] signature)
			throws InstanceNotFoundException, MBeanException,
			ReflectionException {
		return mbeanServer.invoke(name, operationName, params, signature);
	}

	public String getDefaultDomain() {
		return mbeanServer.getDefaultDomain();
	}

	public String[] getDomains() {
		
		return mbeanServer.getDomains();
	}

	public void addNotificationListener(ObjectName name,
			NotificationListener listener, NotificationFilter filter,
			Object handback) throws InstanceNotFoundException {
		mbeanServer.addNotificationListener(name, listener, filter, handback);
		
	}

	public void addNotificationListener(ObjectName name, ObjectName listener,
			NotificationFilter filter, Object handback)
			throws InstanceNotFoundException {
		mbeanServer.addNotificationListener(name, listener, filter, handback);
		
	}

	public void removeNotificationListener(ObjectName name, ObjectName listener)
			throws InstanceNotFoundException, ListenerNotFoundException {
		mbeanServer.removeNotificationListener(name, listener);
		
	}

	public void removeNotificationListener(ObjectName name,
			ObjectName listener, NotificationFilter filter, Object handback)
			throws InstanceNotFoundException, ListenerNotFoundException {
		mbeanServer.removeNotificationListener(name, listener, filter, handback);
		
	}

	public void removeNotificationListener(ObjectName name,
			NotificationListener listener) throws InstanceNotFoundException,
			ListenerNotFoundException {
		mbeanServer.removeNotificationListener(name, listener);
		
	}

	public void removeNotificationListener(ObjectName name,
			NotificationListener listener, NotificationFilter filter,
			Object handback) throws InstanceNotFoundException,
			ListenerNotFoundException {
		mbeanServer.removeNotificationListener(name, listener, filter, handback);
		
	}

	public MBeanInfo getMBeanInfo(ObjectName name)
			throws InstanceNotFoundException, IntrospectionException,
			ReflectionException {
		return mbeanServer.getMBeanInfo(name);
	}

	public boolean isInstanceOf(ObjectName name, String className)
			throws InstanceNotFoundException {
		return mbeanServer.isInstanceOf(name, className);
	}

	public Object instantiate(String className) throws ReflectionException,
			MBeanException {
		return mbeanServer.instantiate(className);
	}

	public Object instantiate(String className, ObjectName loaderName)
			throws ReflectionException, MBeanException,
			InstanceNotFoundException {
		return mbeanServer.instantiate(className, loaderName);
	}

	public Object instantiate(String className, Object[] params,
			String[] signature) throws ReflectionException, MBeanException {
		
		return mbeanServer.instantiate(className, params, signature);
	}

	public Object instantiate(String className, ObjectName loaderName,
			Object[] params, String[] signature) throws ReflectionException,
			MBeanException, InstanceNotFoundException {
		return mbeanServer.instantiate(className, loaderName, params, signature);
	}

	public ObjectInputStream deserialize(ObjectName name, byte[] data)
			throws InstanceNotFoundException, OperationsException {
		return mbeanServer.deserialize(name, data);
	}

	public ObjectInputStream deserialize(String className, byte[] data)
			throws OperationsException, ReflectionException {
		return mbeanServer.deserialize(className, data);
	}

	public ObjectInputStream deserialize(String className,
			ObjectName loaderName, byte[] data)
			throws InstanceNotFoundException, OperationsException,
			ReflectionException {
		return mbeanServer.deserialize(className, loaderName, data);
	}

	public ClassLoader getClassLoaderFor(ObjectName mbeanName)
			throws InstanceNotFoundException {
		return mbeanServer.getClassLoader(mbeanName);
	}

	public ClassLoader getClassLoader(ObjectName loaderName)
			throws InstanceNotFoundException {
		return mbeanServer.getClassLoader(loaderName);
	}

	public ClassLoaderRepository getClassLoaderRepository() {
		return mbeanServer.getClassLoaderRepository();
	}

	public ObjectName preRegister(MBeanServer server, ObjectName name)
			throws Exception {
		log.info("Java version:"+System.getProperty("java.version")+" java vendor:"+System.getProperty("java.vendor"));
		log.info("os name:"+System.getProperty("os.name"));
		return name==null?new ObjectName(this.OBJECT_NAME):name;
	}

	public void postRegister(Boolean registrationDone) {
		
	}

	public void preDeregister() throws Exception {
		
	}

	public void postDeregister() {
		
	}

	public MBeanServer getMBeanServer() {
		
		return mbeanServer;
	}
	
	private void createMx4jHtmlAdapter() throws Exception
	{
		httpAdaptor = new HttpAdaptor();
		XSLTProcessor processor = new XSLTProcessor();
		httpAdaptor.setProcessor(processor);
		if(htmlport>0)
		{
		   httpAdaptor.setPort(htmlport);
		}
		httpAdaptor.setHost(hostip);
		ObjectName httpAdaptorObjName = new ObjectName("Arm:name=xm4jHttpAdaptor");
		ObjectName httpAdaptorProcess = new ObjectName("Arm:name=xm4jHttpAdaptorProcess");
		mbeanServer.registerMBean(httpAdaptor, httpAdaptorObjName);
		mbeanServer.registerMBean(processor, httpAdaptorProcess);
		armMBeanRespository.addMBeanToRespository(httpAdaptorObjName);
		armMBeanRespository.addMBeanToRespository(httpAdaptorProcess);
		httpAdaptor.start();
		log.info("with xm4j of httpAdaptor finished url:http://"+httpAdaptor.getHost()+":"+httpAdaptor.getPort());
	}

}
